package io.wins.vertx.service;

import io.vertx.core.AsyncResult;
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.Vertx;
import io.vertx.core.eventbus.DeliveryOptions;
import io.vertx.core.eventbus.Message;
import io.vertx.core.eventbus.ReplyException;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.core.logging.Logger;
import io.vertx.core.logging.LoggerFactory;
import io.wins.vertx.core.CacheManager;
import io.wins.vertx.database.JdbcExecutor;

public class BaseService {
    protected final Logger logger = LoggerFactory.getLogger(this.getClass());
    public static final String SQL = "sql";
    public static final String PARAMS = "params";
    protected Vertx vertx;

    protected void insertSql(final JsonObject sqlMsg, final Handler<AsyncResult<JsonArray>> next) {
        sqlMsg.put("action", "insert");
        this.executeSql(sqlMsg, next);
    }

    protected void updateSql(final JsonObject sqlMsg, final Handler<AsyncResult<JsonArray>> next) {
        sqlMsg.put("action", "update");
        this.executeSql(sqlMsg, next);
    }

    protected void deleteSql(final JsonObject sqlMsg, final Handler<AsyncResult<JsonArray>> next) {
        sqlMsg.put("action", "delete");
        this.executeSql(sqlMsg, next);
    }

    protected void querySql(final JsonObject sqlMsg, final Handler<AsyncResult<JsonArray>> next) {
        sqlMsg.put("action", "select");
        this.executeSql(sqlMsg, next);
    }

    protected int calcPage(final int page, final int limit) {
        if (page <= 0)
            return 0;
        return limit * (page - 1);
    }

    protected void executeSql(final JsonObject sqlMsg, final Handler<AsyncResult<JsonArray>> next) {
//        this.logger.info("execute sql: " + sqlMsg.getString("sql"));
        this.vertx.eventBus().<JsonArray>send(JdbcExecutor.CONSUMER, sqlMsg, new DeliveryOptions().setSendTimeout(3000),
            message -> {
                if (message.succeeded()) {
                    next.handle(Future.succeededFuture(message.result().body()));
                } else {
                    this.logger.info(message.cause());
                    next.handle(Future.failedFuture(message.cause()));
                }
            });
    }

    protected <T> Handler<AsyncResult<T>> resultUpdate(final Message<?> message, final String failMessage,
                                                       final Object... args) {
        return this.resultUpdate(null, message, failMessage, args);
    }

    protected <T> Handler<AsyncResult<T>> resultUpdate(final String id, final Message<?> message, final String failMessage,
                                                       final Object... args) {
        return r -> {
            if (r.succeeded()) {
                final JsonObject result = new JsonObject();
                result.put("status", "success")
                    .put("id", id)
                    .put("rows", ((JsonArray) r.result()).getInteger(0));
                message.reply(result);
            } else {
                final String failedMessage = String.format(failMessage, args);
                this.logger.error(failedMessage, r.cause());
                message.fail(this.getFailureCode(r.cause()), failedMessage);
            }
        };
    }

    protected <T> Handler<AsyncResult<T>> resultQuery(final Message<JsonObject> message,
                                                      final boolean cache, final String module,
                                                      final String failMessage, final Object... args) {
        return r -> {
            if (r.succeeded()) {
                final JsonArray result = (JsonArray) r.result();
                message.reply(new JsonObject().put("request", message.body()).put("result", result));
                if (cache) {
                    final JsonObject obj = result.getJsonObject(0);
                    CacheManager.getInstance().set(obj.getString("id"), module, obj);
                }
            } else {
                final String failedMessage = String.format(failMessage, args);
                this.logger.error(failedMessage, r.cause());
                message.fail(this.getFailureCode(r.cause()), failedMessage);
            }
        };
    }

    protected <T> Handler<AsyncResult<T>> resultNoRespon(final String failMessage, final Object... args) {
        return r -> {
            if (r.succeeded()) {
                this.logger.info("result handle finished!");
            } else {
                final String failedMessage = String.format(failMessage, args);
                this.logger.error(failedMessage, r.cause());
            }
        };
    }

    protected String buildUpdateSql(final Object Object) {
        final StringBuilder sb = new StringBuilder();
        return sb.toString();
    }

    private int getFailureCode(final Throwable t) {
        if (!(t instanceof ReplyException)) {
            return 1;
        }
        final ReplyException re = (ReplyException) t;
        switch (re.failureType()) {
            case NO_HANDLERS:
                return 2; // no resources found
            case RECIPIENT_FAILURE:
                return re.failureCode(); // internal exception from the underlying verticle
            case TIMEOUT:
                return 3; // timeout
            default:
                return 1; // internal exception
        }
    }
}
